package com.tau.egonetwork.excel;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.ss.usermodel.Comment;
import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import com.tau.egonetwork.exceptions.ExcelCastException;
import com.tau.egonetwork.profiles.FriendsList;
import com.tau.egonetwork.profiles.Group;
import com.tau.egonetwork.profiles.MainProfile;
import com.tau.egonetwork.profiles.Profile;

public class ExcelWriteAnalysisUtils {
	
	private static final String SHEET_CONNECTIONS = "Connections";
	private static final String SHEET_STATISTICS = "Statistics";
	
	private static final String STATS_COLUMN_NAME = "Name";
	private static final String STATS_COLUMN_DEGREE = "Degree";
	private static final String STATS_COLUMN_COEFFS = "Clustering Coeffient";
	private static final String STATS_COLUMN_COEFFS_NO_ROOT = "Clustering Coeffient*";
	private static final String STATS_COLUMN_KSHELL = "K-Shell Decomposition";
	private static final String STATS_COLUMN_KSHELL_NO_ROOT = "K-Shell Decomposition*";
	private static final String STATS_COLUMN_SHORTEST_PATH = "Mean Shortest Paths";
	private static final String STATS_COLUMN_GROUP = "Group";
	
	private static final String STATS_NOTE = "Not incluidng the egocenteric node";

	public static <T extends Comparable<T>> void saveStatistics(String filename,
			MainProfile<T> profile, Map<T, Double> coeffients,
			Map<T, Double> coeffientsNoRoot, Map<T, Integer> shells,
			Map<T, Integer> shellsNoRoot, Map<T, Double> meanShortestPaths) {
		
		try {

			Workbook w;
			if (filename.endsWith(".xlsx")) {
				w = new XSSFWorkbook();
			} else if (filename.endsWith(".xls")) {
				w  = new HSSFWorkbook();
			} else {
				throw new ExcelCastException("Can only create either .xls or .xlsx files.");
			}
			
			createConnectionsSheet(w, profile);
			createStatisticsSheet(w, profile, coeffients, coeffientsNoRoot, shells, shellsNoRoot, meanShortestPaths);
		
			FileOutputStream out = new FileOutputStream(new File(filename));
			w.write(out);
			out.close();
			
		} catch (IOException e) {
			throw new ExcelCastException("Could not create file " + filename + ".", e);
		} catch (Exception e) {
			throw new ExcelCastException("Error occurred while trying to write statistics to excel file.", e);
		}
		
	}
	
	private static <T extends Comparable<T>> void createConnectionsSheet(
			Workbook w, MainProfile<T> profile) {
		
		Sheet sheet = w.createSheet(SHEET_CONNECTIONS);

		if (profile.getFriendsList() == null)
			return;
		
		//build connections matrix to be displayed in the sheet:
		int numOfConnections = profile.getFriendsList().size();
		int[][] mat = new int[numOfConnections][numOfConnections];
		
		FriendsList<T> friends = profile.getFriendsList();
		for (int i = 0; i < numOfConnections; i++) {
			
			Profile<T> friend = friends.get(i);
			FriendsList<T> mutualFriends = friend.getFriendsList();
			if (mutualFriends == null)
				continue;
			
			int mutual_size = mutualFriends.size();
			
			for (int j = 0; j < mutual_size; j++) {
				
				Profile<T> mutual = mutualFriends.get(j);
				int globalIndexOfJ = findProfile(friends, mutual.getID());
				
				mat[i][globalIndexOfJ] = 1;
			}
			
		}
		
		//first create the header:
		int rowNum = 0;
		Row firstRow = sheet.createRow(rowNum++);
		
		//create first cell and put profile name in it:
		int cellNum = 0;
		Cell nameCell = firstRow.createCell(cellNum++);
		nameCell.setCellValue(profile.getName());
		
		CellStyle cellStyle = w.createCellStyle();
		cellStyle.setFillForegroundColor(IndexedColors.LIGHT_YELLOW.index);
		cellStyle.setFillPattern(CellStyle.SOLID_FOREGROUND);
		cellStyle.setBorderBottom(CellStyle.BORDER_THIN);
		cellStyle.setBorderLeft(CellStyle.BORDER_THIN);
		cellStyle.setBorderRight(CellStyle.BORDER_THIN);
		cellStyle.setBorderTop(CellStyle.BORDER_THIN);
		
		//put friends' names in header:
		for (int i = 0; i < numOfConnections; i++) {
			Cell cell = firstRow.createCell(cellNum++);
			cell.setCellValue(friends.get(i).getName());
			cell.setCellStyle(cellStyle);
		}
		
		//now for each friend create row and fill matrix data:
		for (int i = 0; i < numOfConnections; i++) {
			
			Row row = sheet.createRow(rowNum++);
			cellNum = 0;
			
			//create first column cell:
			Cell cell = row.createCell(cellNum++);
			cell.setCellValue(friends.get(i).getName());
			cell.setCellStyle(cellStyle);
						
			for (int j = 0; j < numOfConnections; j++) {
				row.createCell(cellNum++).setCellValue(mat[i][j]);
			}
		}
		
		sheet.createFreezePane(1, 1);
		for (int i = 0; i <= numOfConnections; i++) {
			sheet.autoSizeColumn(i);
		}
		
	}
	
	private static <T extends Comparable<T>> void createStatisticsSheet(Workbook w, MainProfile<T> profile,
			Map<T, Double> coeffients, Map<T, Double> coeffientsNoRoot, Map<T, Integer> shells,
			Map<T, Integer> shellsNoRoot, Map<T, Double> meanShortestPaths) {
		
		Sheet sheet = w.createSheet(SHEET_STATISTICS);
		
		//create headers:
		int rowNum = 0;
		Row header = sheet.createRow(rowNum++);
		
		int cellNum = 0;

		CellStyle headerCellStyle = w.createCellStyle();
		headerCellStyle.setFillForegroundColor(IndexedColors.LIGHT_YELLOW.index);
		headerCellStyle.setFillPattern(CellStyle.SOLID_FOREGROUND);
		headerCellStyle.setBorderBottom(CellStyle.BORDER_THIN);
		headerCellStyle.setBorderTop(CellStyle.BORDER_THIN);
		headerCellStyle.setBorderLeft(CellStyle.BORDER_THIN);
		headerCellStyle.setBorderRight(CellStyle.BORDER_THIN);
		headerCellStyle.setAlignment(CellStyle.ALIGN_CENTER);
		Font headerFont = w.createFont();
		headerFont.setBoldweight(Font.BOLDWEIGHT_BOLD);
		headerCellStyle.setFont(headerFont);
		
		int numOfColumns = 8;
		header.createCell(cellNum++).setCellValue(STATS_COLUMN_NAME);
		header.createCell(cellNum++).setCellValue(STATS_COLUMN_DEGREE);
		header.createCell(cellNum++).setCellValue(STATS_COLUMN_COEFFS);
		header.createCell(cellNum++).setCellValue(STATS_COLUMN_COEFFS_NO_ROOT);
		header.createCell(cellNum++).setCellValue(STATS_COLUMN_KSHELL);
		header.createCell(cellNum++).setCellValue(STATS_COLUMN_KSHELL_NO_ROOT);
		header.createCell(cellNum++).setCellValue(STATS_COLUMN_SHORTEST_PATH);
		header.createCell(cellNum++).setCellValue(STATS_COLUMN_GROUP);
		
		Drawing drawing = sheet.createDrawingPatriarch();
		ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 4, 2, 6, 5);
		
		int[] commentColumns = { 1, 3, 5 };
		for (int i = 0; i < commentColumns.length; i++) {
			Comment comment = drawing.createCellComment(anchor);
			comment.setString(w.getCreationHelper().createRichTextString(STATS_NOTE));			
			header.getCell(commentColumns[i]).setCellComment(comment);			
		}
				
		for (int i = 0; i < numOfColumns; i++) {
			header.getCell(i).setCellStyle(headerCellStyle);
		}
		
		//create data for egocenteric:
		Row egoRow = sheet.createRow(rowNum++);
		cellNum = 0;
		egoRow.createCell(cellNum++).setCellValue(profile.getName());
		if (profile.getFriendsList() != null) {
			egoRow.createCell(cellNum++).setCellValue(profile.getFriendsList().size());
		} else {
			egoRow.createCell(cellNum++).setCellValue(0);
		}
		egoRow.createCell(cellNum++).setCellValue(coeffients.get(profile.getID()));
		egoRow.createCell(cellNum++); //no value for the egocenteric node -- leave empty
		egoRow.createCell(cellNum++).setCellValue(shells.get(profile.getID()));
		egoRow.createCell(cellNum++); //no value ofr the egocenteric node -- leave empty
		egoRow.createCell(cellNum++).setCellValue(meanShortestPaths.get(profile.getID()));
		egoRow.createCell(cellNum++); //no vaue for the egocenteric node -- leave empty
		
		if (profile.getFriendsList() == null)
			return;
		
		//for each friend in friend's list, fill statistical data:
		for (Profile<T> p : profile.getFriendsList()) {
			
			Row row = sheet.createRow(rowNum++);
			cellNum = 0;
			
			row.createCell(cellNum++).setCellValue(p.getName());
			if (p.getFriendsList() != null) {
				row.createCell(cellNum++).setCellValue(p.getFriendsList().size());
			} else {
				row.createCell(cellNum++).setCellValue(0);
			}
			row.createCell(cellNum++).setCellValue(coeffients.get(p.getID()));
			row.createCell(cellNum++).setCellValue(coeffientsNoRoot.get(p.getID()));
			row.createCell(cellNum++).setCellValue(shells.get(p.getID()));
			row.createCell(cellNum++).setCellValue(shellsNoRoot.get(p.getID()));
			if (meanShortestPaths != null)
				row.createCell(cellNum++).setCellValue(meanShortestPaths.get(p.getID()));
			
			if (profile.getGroupsList() != null) {
				for (Group<T> g : profile.getGroupsList()) {
					if (g.contains(p)) {
						row.createCell(cellNum++).setCellValue(g.getGroupName());
						break;
					}
				}
			}

		}
				
		sheet.createFreezePane(1, 1);
		for (int i = 0; i < numOfColumns; i++) {
			sheet.autoSizeColumn(i);
		}

	}
	
	private static <T extends Comparable<T>> int findProfile(FriendsList<T> friends, T idToFind) {
		int size = friends.size();
		for (int i = 0; i < size; i++) {
			if (friends.get(i).getID().equals(idToFind))
					return i;
				
		}
		return -1;
	}

}
